package edu.columbia.plt.visage;



import java.util.*;
import java.util.List;
import java.awt.*;

import edu.columbia.plt.visage.Agent;
import edu.columbia.plt.visage.Cell;
import edu.columbia.plt.visage.Matrix;
import edu.columbia.plt.visage.interfaces.IAgentDriver;


/*
 * Class AgentDriver
 * 
 * By Visage Team
 * AgentDriver is a subclass of Agent. It can be aware of environment information
 * like cell and matrix. It is also a thread implemation of Agent.
 * 
 */

public class AgentDriverTemplate extends Agent implements Runnable, IAgentDriver {
	/**
	 * @param cell
	 */

	private Cell cell; // which cell the agent belongs to

	// construction function
	public AgentDriverTemplate(String type) {
		// default color, shape?
		super(type);
		this.x = Math.random() * Matrix.getWidth();
		this.y = Math.random() * Matrix.getHeight();
		cell = Matrix.getCellByIndex(x, y);
		cell.addAgent(this);
		Matrix.addAgent(this);
	}

	public AgentDriverTemplate(AgentDriverTemplate agent) {
		super(agent);
		this.x = agent.x;
		this.y = agent.y;
		cell = Matrix.getCellByIndex(x, y);
		cell.addAgent(this);
		Matrix.addAgent(this);
	}

	// gets func
	/* (non-Javadoc)
	 * @see visage.IAgentDriver#getCell()
	 */
	public Cell getCell() {
		return cell;
	}

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#produce(int, java.lang.String)
	 */
	public Collection<Agent> produce(int number, String type) {
		Collection<Agent> set = new HashSet<Agent>();
		for (int i = 0; i < number; i++) {
			AgentDriverTemplate agent = new AgentDriverTemplate(this);
			agent.become(type);
			set.add(agent);
			Thread t = new Thread(agent);
			t.start();
		}
		return set;
	} // produce <number> agents with type

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#reproduce(int)
	 */
	public Collection<Agent> reproduce(int number) {
		Collection<Agent> set = new HashSet<Agent>();
		for (int i = 0; i < number; i++) {
			AgentDriverTemplate agent = new AgentDriverTemplate(this);
			set.add(agent);
			Thread t = new Thread(agent);
			t.start();
		}
		return set;
	} // reproduce <number> agents

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#die()
	 */
	public void die() {
		this.freeze();
		cell.removeAgentByID(ID);
		Matrix.removeAgentByID(ID);
		isDie = true;
		// this.interrupt();

	} // kill itself ??

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#move()
	 */
	public void move() {
		x += speed * Math.cos(Math.toRadians(direction));
		if (x < 0)
			x += Matrix.getWidth();
		if (x > Matrix.getWidth())
			x -= Matrix.getWidth();
		y -= speed * Math.sin(Math.toRadians(direction));
		if (y < 0)
			y += Matrix.getHeight();
		if (y > Matrix.getHeight())
			y -= Matrix.getHeight();
		if (Matrix.getCellByIndex(x, y) != cell) {
			cell.removeAgentByID(ID);
			cell = Matrix.getCellByIndex(x, y);
			cell.addAgent(this);
		}
		
		if ( super.track ) {
			super.appendTrackHistory(x, y);
		}

	} // move in the direction with certain speed

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#moverandom()
	 */
	public void moverandom() {
		this.turnRight((int)(60*Math.random()-30));
		move();
		/*
		double tmpSpeed = speed * Math.random();
		x += tmpSpeed * Math.cos(Math.toRadians(360 * Math.random()));
		if (x < 0)
			x += Matrix.getWidth();
		if (x > Matrix.getWidth())
			x -= Matrix.getWidth();
		y += tmpSpeed * Math.sin(Math.toRadians(360 * Math.random()));
		if (y < 0)
			y += Matrix.getHeight();
		if (y > Matrix.getHeight())
			y -= Matrix.getHeight();
		// System.out.println("x: "+x+", y: "+y);
		if (Matrix.getCellByIndex(x, y) != cell) {
			cell.removeAgentByID(ID);
			cell = Matrix.getCellByIndex(x, y);
			cell.addAgent(this);
		}

		if ( super.track ) {
			super.appendTrackHistory(x*Matrix.getCellSize(), y*Matrix.getCellSize());
			
		}
		*/
	}

	// set agent position
	/* (non-Javadoc)
	 * @see visage.IAgentDriver#setPosition(double, double)
	 */
	public void setPosition(double x, double y) {
		this.x = x;
		this.y = y;
		if (Matrix.getCellByIndex(this.x, this.y) != cell) {
			cell.removeAgentByID(ID);
			cell = Matrix.getCellByIndex(x, y);
			cell.addAgent(this);
		}
		
		if ( super.track ) {
			super.appendTrackHistory(x*Matrix.getCellSize(), y*Matrix.getCellSize());
		}
	}

	// kill agents
	/* (non-Javadoc)
	 * @see visage.IAgentDriver#kill(java.util.Collection)
	 */
	public void kill(Collection<Agent> agents) {
			if (isDie)
			return;
		Iterator<Agent> it = agents.iterator();
		while (it.hasNext()) {
			Agent agent = it.next();
			//System.out.println("Agent Type: " + agent.type + ", ID: "+ agent.getID() + "killed!");
			agent.freeze();
			agent.die();
			//System.out.println("Number of Agents: "+Matrix.getAgentSet().size(
			// ));
		}
	}
	
	public void kill(Agent agent) {
			if (isDie)
			return;
			agent.freeze();
			agent.die();
	}
	
	public Collection<Agent> getOthers(Collection<Agent> agents){
		if (agents == null)
			return null;
		else{
			agents.remove(this);
			return agents;
		}
	}
	
	public void become(String type) {
		if (this.type != type) {
			Matrix.removeAgentByID(this.ID);
			this.type = type;
			Matrix.addAgent(this);
			System.out.println("become "+this.type+":"+Matrix.getAgentSetByType(this.type).size());
			}

		
	}

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#run()
	 */
	public void run() {
		
		// initial header
		
		// initial footer
		
		while (true) {
			if (isDie) {
				//System.out.printf("Agent ID: %d type: %s died\n.", ID, type);
				return;
			}
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.logic();
			// this.print();
			//System.out.println("Number of Agents: "+Matrix.getAgentSet().size(
			// ));
		}
	}

	/* (non-Javadoc)
	 * @see visage.IAgentDriver#logic()
	 */
	public void logic() {
		// code header
		
		// code footer

	} // do logic inside tell

}
